/*
 * $Id: GraphUtils.java 40 2013-01-08 04:03:14Z rob@patientsknowbest.com $ 
 */
package com.representqueens.spark;

import java.awt.geom.Rectangle2D;
import java.util.List;

import com.representqueens.spark.util.GraphInfo;

/*
 * 
 * Copyright 2006 Larry Ogrodnek
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author Larry Ogrodnek <larry@cheesesteak.net>
 * @author Rob Whelan <rob@patientsknowbest.com>
 * @version $Revision: 40 $ $Date: 2013-01-08 04:03:14 +0000 (Tue, 08 Jan 2013) $
 */
final class GraphUtils {
    private GraphUtils() {
    }
    
    static GraphInfo getGraphInfo(final List<DataPoint> data, final SizeParams size) {
    	GraphInfo info = new GraphInfo();
    	info.size = size;
    	info.rangesFound = false;

    	boolean forceXMin = size.getForceXMin() != null;
    	boolean forceXMax = size.getForceXMax() != null;
    	info.xMin = forceXMin ? size.getForceXMin() : Double.MAX_VALUE;
    	info.xMax = forceXMax ? size.getForceXMax() : Double.MIN_VALUE;

    	boolean forceYMin = size.getForceYMin() != null;
    	boolean forceYMax = size.getForceYMax() != null;
    	info.yMin = forceYMin ? size.getForceYMin() : Double.MAX_VALUE;
    	info.yMax = forceYMax ? size.getForceYMax() : Double.MIN_VALUE;

    	info.singlePoint = (data.size() == 1);
    	
    	for (int i=0; i<data.size(); i++) {
    		final DataPoint dp = data.get(i);
    		
    		// details on range values
    		if( dp.hasRange() ) {
    			info.rangesFound = true;
    			if (info.firstRangedIndex == null)
    				info.firstRangedIndex = i;
    			
    			info.lastRangedIndex = i; // keep overwriting... 
    		}

    		// y axis min/max (if these aren't forced)
    		// min/max y values may be in the displayed RANGE, not just in the value
    		if( ! forceYMin ) {
    			info.yMin = Math.min(info.yMin, dp.getY());
    			if( dp.hasRange() )
    				info.yMin = Math.min(info.yMin, dp.getRangeLow());
    		}
    		if( ! forceYMax ) {
    			info.yMax = Math.max(info.yMax, dp.getY());
    			if( dp.hasRange() )
    				info.yMax = Math.max(info.yMax, dp.getRangeHigh());
    		}

    		// x axis min/max (if these aren't forced)
    		if( ! forceXMin )
    			info.xMin = Math.min(info.xMin, dp.getX());
    		if( ! forceXMax )
    			info.xMax = Math.max(info.xMax, dp.getX());
    	}

    	// calculate y axis scale
    	if (info.yMax <= info.yMin) {
    		info.yScale = 1.0d;
    	}
    	else {
    		info.yScale = size.getHeight() / (info.yMax - info.yMin);
    	}

    	// calculate x axis scale
    	if (info.xMax <= info.xMin) {
    		info.xScale = 1.0d;
    	}
    	else {
    		info.xScale = size.getWidth() / (info.xMax - info.xMin);
    	}

    	return info;
    }

    public static Rectangle2D.Double getRectangleAroundPoint(List<DataPoint> data, int i, GraphInfo info) {
    	DataPoint p = data.get(i);
		double thisX = getGraphX(p.getX(), info);
		
		double rectX = 0; // default if there's no previous point
		if( i > 0 ) {
			// start at a point a bit more than halfway to the prev point
			double prevX = getGraphX(data.get(i-1).getX(), info);
			rectX = (prevX + thisX) / 2d;
			rectX -= Math.min( (thisX-prevX) / 8, 3); // add on a bit -- not more than 3 pixels
		}
		
		double rectW = info.size.getImgWidth() - rectX; // default: to end of image
		if( i < data.size()-1 ) {
			// end the rect a bit more than halfway to the next point
			double nextX = getGraphX(data.get(i+1).getX(), info);
			rectW = ((thisX+nextX) / 2d) - rectX;
			rectW += Math.min( (nextX-thisX) / 8, 3); // add on a bit -- not more than 3 pixels
		}

		return new Rectangle2D.Double(rectX, 0, rectW, info.size.getImgHeight());
    }
    
    public static double getGraphX(double x, GraphInfo info) {
    	return (x-info.xMin) * info.xScale + SizeParams.PADDING;
    }
    
    public static double getGraphY(double y, GraphInfo info) {
    	// getHeight() has PADDING*1 subtracted from it... we just want PADDING subtracted, so add PADDING*1
    	return info.size.getHeight() - ((y - info.yMin) * info.yScale) + SizeParams.PADDING;
    }
}
